

module activation #(
    parameter P_CH   = 4,
    parameter N_CH   = 16,
    parameter B_BIT  = 16,
    parameter M_BIT  = 8,
    parameter A_BIT  = 8,
    parameter B_FILE = "",
    parameter M_FILE = ""
) (
    input  logic                  clk,
    input  logic                  rst_n,
    input  logic                  in_valid,
    output logic                  in_ready,
    input  logic [P_CH*B_BIT-1:0] in_data,
    output logic                  out_valid,
    input  logic                  out_ready,
    output logic [P_CH*A_BIT-1:0] out_data
);

    localparam FOLD = N_CH / P_CH;
    logic [  P_CH*B_BIT-1:0] in_data_d1;
    logic [$clog2(FOLD)-1:0] bias_addr_d0;
    logic [$clog2(FOLD)-1:0] mult_addr_d0;
    logic [  P_CH*B_BIT-1:0] bias_data_d1;
    logic [  P_CH*M_BIT-1:0] mult_data_d1;
    logic [$clog2(FOLD)-1:0] f_cnt;
    logic                    pipe_en_in;
    logic                    pipe_en_out;
    logic                    pipe_en;
    logic                    data_vld_d1;
    logic                    data_vld_d2;

    rom #(
        .DWIDTH   (P_CH * B_BIT),
        .AWIDTH   ($clog2(FOLD)),
        .MEM_SIZE (FOLD),
        .INIT_FILE(B_FILE)
    ) u_bias_rom (
        .clk  (clk),
        .ce0  (pipe_en_out),
        .addr0(bias_addr_d0),
        .q0   (bias_data_d1)
    );

    rom #(
        .DWIDTH   (P_CH * M_BIT),
        .AWIDTH   ($clog2(FOLD)),
        .MEM_SIZE (FOLD),
        .INIT_FILE(M_FILE)
    ) u_mult_rom (
        .clk  (clk),
        .ce0  (pipe_en_out),
        .addr0(mult_addr_d0),
        .q0   (mult_data_d1)
    );

    assign bias_addr_d0 = f_cnt;
    assign mult_addr_d0 = f_cnt;
    assign pipe_en_in   = in_valid;
    assign pipe_en_out  = out_ready;
    assign pipe_en      = pipe_en_in && pipe_en_out;
    assign in_ready     = out_ready;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            f_cnt <= '0;
        end else if (pipe_en) begin
            if (f_cnt == FOLD - 1) begin
                f_cnt <= '0;
            end else begin
                f_cnt <= f_cnt + 1;
            end
        end
    end

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_vld_d1 <= 1'b0;
            data_vld_d2 <= 1'b0;
            in_data_d1  <= '0;
        end else begin
            if (pipe_en_out) begin
                data_vld_d1 <= in_valid;
                data_vld_d2 <= data_vld_d1;
                in_data_d1  <= in_data;
            end
        end
    end

    logic signed [B_BIT-1:0] in_ch  [P_CH];
    logic signed [B_BIT-1:0] bias_ch[P_CH];
    logic        [M_BIT-1:0] mult_ch[P_CH];
    logic        [A_BIT-1:0] out_ch [P_CH];

    always_comb begin
        for (int i = 0; i < P_CH; i++) begin
            in_ch[i]   = in_data_d1[i*B_BIT+:B_BIT];
            bias_ch[i] = bias_data_d1[i*B_BIT+:B_BIT];
            mult_ch[i] = mult_data_d1[i*M_BIT+:M_BIT];
        end
    end

    generate
        for (genvar i = 0; i < P_CH; i++) begin : gen_actv_requant
            bias_mul #(
                .B_BIT(B_BIT),
                .M_BIT(M_BIT),
                .A_BIT(A_BIT)
            ) u_bias_mul (
                .clk     (clk),
                .rst_n   (rst_n),
                .en      (pipe_en_out),
                .in_data (in_ch[i]),
                .bias    (bias_ch[i]),
                .mult    (mult_ch[i]),
                .out_data(out_ch[i])
            );
        end
    endgenerate

    assign out_valid = data_vld_d2;
    always_comb begin
        for (int o = 0; o < P_CH; o++) begin
            out_data[o*A_BIT+:A_BIT] = out_ch[o];
        end
    end

endmodule
